﻿using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace McHotspoot
{
    public class RProject : McHotspotI
    {
        public string execute(string key, double n, double index, string codein, out string message)
        {
            string directory = RoleEnvironment.GetLocalResource("McCloudStorage").RootPath;            
            string code = Path.Combine(directory, "codein_" + key + index + ".R");
            string output = Path.Combine(directory, "codein_" + key + index + ".csv");
            string feedback = Path.Combine(directory, "codein_" + key + index + ".out");
            string bat = Path.Combine(directory, "codein_" + key + index + ".bat");

            codein += "\r\n n<-" + n;
            codein += "\r\n seed<-" + (1982 + (n * 2 * index)).ToString();
            codein += "\r\n output<-\"" + output.Replace("\\", "\\\\") + "\"";
            codein += "\r\n process(n, seed, output)";
            codein += "\r\n q()";

            FileStream fileStream = new FileStream(code, FileMode.Create);
            byte[] bytes = Encoding.Default.GetBytes(codein);
            fileStream.Write(bytes, 0, bytes.Length);
            fileStream.Close();

            exec(code, feedback, bat, directory, out message);

            FileStream fileStream2 = new FileStream(output, FileMode.Open);
            StreamReader reader = new StreamReader(fileStream2);
            string r = reader.ReadToEnd();
            reader.Close();
            fileStream.Close();

            return r;
        }

        public string finish(string key, double n, string r, string codein, string codeout, out string message)
        {
            string directory = RoleEnvironment.GetLocalResource("McCloudStorage").RootPath;
            string code = Path.Combine(directory, "codeout_" + key + ".R");
            string output = Path.Combine(directory, "codeout_" + key + ".csv");
            string feedback = Path.Combine(directory, "codeout_" + key + ".out");
            string bat = Path.Combine(directory, "codeout_" + key + ".bat");

            codeout += "\r\n n<-" + n;
            codeout += "\r\n input<-\"" + r.Replace("\\", "\\\\") + "\"";
            codeout += "\r\n output<-\"" + output.Replace("\\", "\\\\") + "\"";
            codeout += "\r\n merge(n, input, output)";
            codeout += "\r\n q()";

            FileStream fileStream = new FileStream(code, FileMode.Create);
            byte[] bytes = Encoding.Default.GetBytes(codeout);
            fileStream.Write(bytes, 0, bytes.Length);
            fileStream.Close();

            exec(code, feedback, bat, directory, out message);

            FileStream fileStream2 = new FileStream(output, FileMode.Open);
            StreamReader reader = new StreamReader(fileStream2);
            string m = reader.ReadToEnd();
            reader.Close();
            fileStream.Close();

            return m;
        }

        public void optimization(double n, string codein, string codeout, double npt, 
            out double ninstancesadded, out double ntasks, out int timeoutInSeconds)
        {
            if (npt != 0)
            {
                ntasks = (double)Math.Truncate((decimal)(n / npt));
                if (n > (ntasks * npt)) ntasks++;
            }
            else { ntasks = 19; }            
            ninstancesadded = 0;
            timeoutInSeconds = 2 * 60 * 60;
        }

        public string test(string key, double n, string codein, string codeout, double npt)
        {
            bool ok = true;
            if (n < 0) ok = false;
            if (Math.Truncate(n) < n) ok = false;
            if (npt < 0) ok = false;
            if (Math.Truncate(npt) < npt) ok = false;
            if (npt > n) ok = false;            
            if(ok) return "";
            else return "Error: Input error!";

            string message = "";
            double x = 2;
            string r = execute(key, x, 1, codein, out message);
            string filesRoot = RoleEnvironment.GetLocalResource("McCloudStorage").RootPath;
            string file = Path.Combine(filesRoot, key + ".temp");
            FileStream fileStream = new FileStream(file, FileMode.Create);
            byte[] bytes = Encoding.Default.GetBytes(r);
            fileStream.Write(bytes, 0, bytes.Length);
            fileStream.Close();
            string m = finish(key, x, file, codein, codeout, out message);
            if (m != "") return "";
            else return "Error: Null Result";

            return "";
        }

        private void exec(string code, string feedback, string bat, string directory, out string message)
        {
            string rproject = "";
            if (RoleEnvironment.GetConfigurationSettingValue("Emulator") == "true")
            {
                rproject = @"C:\RProject\bin\i386\Rterm.exe";
            }
            else
            {
                rproject = Path.Combine(directory, @"RProject\bin\x64\Rterm.exe");
            }

            while (!System.IO.File.Exists(rproject)) System.Threading.Thread.Sleep(60000); // Wait 1 minute   

            if (!System.IO.File.Exists(rproject))
            {
                message = "Code not found in: " + code;
                return;
            }

            string command = rproject;
            command += " --no-restore --no-save <";
            command += code;
            command += "> ";
            command += feedback;
            command += " 2>&1 ";

            FileStream fileStream = new FileStream(bat, FileMode.Create);            
            byte[] bytes = Encoding.Default.GetBytes(command);
            fileStream.Write(bytes, 0, bytes.Length);
            fileStream.Close();

            System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
            psi.FileName = bat;
            psi.CreateNoWindow = true;
            psi.ErrorDialog = false;
            psi.UseShellExecute = false;
            psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardInput = false;
            psi.RedirectStandardError = true;
                     
            System.Diagnostics.Process exeProcess = System.Diagnostics.Process.Start(psi);
            exeProcess.PriorityClass = System.Diagnostics.ProcessPriorityClass.High;
            exeProcess.OutputDataReceived +=
                new System.Diagnostics.DataReceivedEventHandler(processOutputHandler);
            exeProcess.BeginOutputReadLine();
            string errString = exeProcess.StandardError.ReadToEnd();
            exeProcess.WaitForExit();
            exeProcess.Close();

            message = "Exec: " + psi.FileName + "\r\n";
            if (errString != "") message += "Exec Error: " + errString + "\r\n";
            if (_output != "") message += "Exec Output: " + _output;

            FileStream fileStream2 = new FileStream(feedback, FileMode.Open);
            StreamReader reader = new StreamReader(fileStream2);
            message += "Exec Response: " + reader.ReadToEnd();
            reader.Close();
            fileStream2.Close();
        }

        private string _output = "";
        private void processOutputHandler(object sendingProcess, 
            System.Diagnostics.DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data)) _output += outLine.Data+"\r\n";
        }
    }
}